package com.wh.wisdomsite.xbox.core.builder.impl;

import com.wh.wisdomsite.xbox.common.*;
import com.wh.wisdomsite.xbox.core.builder.RequestContextBuilder;
import com.wh.wisdomsite.xbox.core.impl.DefaultXboxRequestContext;
import com.wh.wisdomsite.xbox.common.annotation.ExecutionType;
import com.wh.wisdomsite.xbox.common.annotation.HttpAction;
import com.wh.wisdomsite.xbox.common.annotation.MessageFormat;
import com.wh.wisdomsite.xbox.common.config.SystemParameterNames;
import com.wh.wisdomsite.xbox.utils.json.JosnConverter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;


/**
 * 声明DefaultRequestContextBuilder接口实现, 从WebRequest中获取参数,DefaultXboxRequestContext
 * @author 谢泽鹏
 * @version V1.0
 * @Package com.wh.wisdomsite.xbox.core.builder
 * @date 2014年10月8日 下午4:02:14
 * @Copyright 个人版权所有
 * @Company 贵州万华科技有限公司Copyright (c) 2014
 */
public class DefaultRequestContextBuilder implements RequestContextBuilder {

    private static final Log logger = LogFactory.getLog(DefaultRequestContextBuilder.class);


    /**
     * 验证器对象
     */
    private Validator validator;


    /**
     * xml转换器
     */
    private JosnConverter xmlConverter;

    /**
     * json转换器
     */
    private JosnConverter jackJosnConverter;

    /**
     * 构造函数
     */
    public DefaultRequestContextBuilder() {
    }


    /**
     * 构造函数
     *
     * @param jackJosnConverter json转换器
     * @param xmlConverter      xml转换器
     */
    public DefaultRequestContextBuilder(JosnConverter jackJosnConverter, JosnConverter xmlConverter) {
        this.jackJosnConverter = jackJosnConverter;
        this.xmlConverter = xmlConverter;
    }


    /**
     * 构建默认请求上下文
     * @param xboxContext
     * @param request
     * @return
     */
    @Override
    public DefaultXboxRequestContext buildBySysParams(XboxContext xboxContext, HttpServletRequest request) {
        DefaultXboxRequestContext xboxRequestContext = new DefaultXboxRequestContext(xboxContext);
        //设置请求对象及参数列表
        xboxRequestContext.setServletRequest(request);
        xboxRequestContext.setAllParams(getRequestParams(request));
        xboxRequestContext.setIp(getRemoteAddr(request));
        //设置服务的系统级别参数
        xboxRequestContext.setMethod(request.getParameter(SystemParameterNames.getMethod()));
        xboxRequestContext.setVersion(request.getParameter(SystemParameterNames.getVersion()));
        xboxRequestContext.setFormat(getFormat(request));
        xboxRequestContext.setMessageFormat(getResponseFormat(request));
        xboxRequestContext.setExecutionType(getExecutionType(request).name());
        xboxRequestContext.setHttpAction(HttpAction.fromValue(request.getMethod()));
        xboxRequestContext.setLocale(getLocale(request));
        xboxRequestContext.setSign(request.getParameter(SystemParameterNames.getSign()));
        xboxRequestContext.setAppKey(request.getParameter(SystemParameterNames.getAppKey()));
        xboxRequestContext.setSessionId(request.getParameter(SystemParameterNames.getSessionId()));
        if (null != request.getParameter(SystemParameterNames.getRequestTimestamp())) {
            xboxRequestContext.setRequestTimestamp(Long.valueOf(request.getParameter(SystemParameterNames.getRequestTimestamp())));
        }
        xboxRequestContext.setCallType(request.getParameter(SystemParameterNames.getCallType()));
        xboxRequestContext.setCallVersion(request.getParameter(SystemParameterNames.getCallVersion()));
        xboxRequestContext.setClient(request.getParameter(SystemParameterNames.getClient()));
        xboxRequestContext.setAppBrand(request.getParameter(SystemParameterNames.getAppBrand()));
        xboxRequestContext.setAppBaseInfo(request.getParameter(SystemParameterNames.getAppBaseInfo()));

        //设置业务对象参数
        xboxRequestContext.setBindObject(request.getParameter(SystemParameterNames.getBindObject()));
        //设置服务处理器
        ServiceMethodHandler serviceMethodHandler = xboxContext.getServiceMthodHandler(xboxRequestContext.getMethod(), xboxRequestContext.getVersion());
        xboxRequestContext.setServiceMethodHandler(serviceMethodHandler);
        return xboxRequestContext;
    }


    @Override
    public void bindBusinessParams(DefaultXboxRequestContext xboxRequestContext) {
        AbstractXboxRequest xboxRequest = null;
        //服务方法需要参数
        if (xboxRequestContext.getServiceMethodHandler().isXboxRequestImplType()) {
            BindingResult bindingResult = doBind(xboxRequestContext.getServletRequest(), xboxRequestContext.getServiceMethodHandler().getRequestType(), xboxRequestContext.getMessageFormat());
            xboxRequestContext.setAttribute(DefaultXboxRequestContext.SPRING_VALIDATE_ERROR_ATTRNAME, bindingResult.getAllErrors());
            xboxRequest = (AbstractXboxRequest) bindingResult.getTarget();
        }
        //服务方法不需要参数
        else {
            xboxRequest = new DefaultXboxRequest();
        }
        xboxRequest.setXboxRequestContext(xboxRequestContext);
        xboxRequestContext.setXboxRequest(xboxRequest);
    }

    /**
     * 获取响应格式.默认为xml
     * @param request
     * @return
     */
    public static MessageFormat getResponseFormat(HttpServletRequest request) {
        String messageFormat = request.getParameter(SystemParameterNames.getFormat());
        if (MessageFormat.isVaildFormat(messageFormat)) {
            return MessageFormat.getFormat(messageFormat);
        } else {
            return MessageFormat.xml;
        }
    }


    /**
     * 获取执行类型.默认为SYNC
     * @param request
     * @return
     */
    public static ExecutionType getExecutionType(HttpServletRequest request) {
        String executionType = request.getParameter(SystemParameterNames.getExecutionType());
        return ExecutionType.fromValue(executionType);
    }

    /**
     * 获取语言.默认为中文
     *
     * @param request
     * @return
     */
    public static Locale getLocale(HttpServletRequest request) {
        String localeStr = null;
        try {
            localeStr = request.getParameter(SystemParameterNames.getLocale());
            if (localeStr == null) {
                return Locale.SIMPLIFIED_CHINESE;
            } else {
                Locale locale = StringUtils.parseLocaleString(localeStr);
                if (isValidLocale(locale)) {
                    return locale;
                }
            }
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("Input the locale:" + localeStr + "can't be converted to a Locale object!");
            }
        }

        return Locale.SIMPLIFIED_CHINESE;
    }

    private static boolean isValidLocale(Locale locale) {
        if (Locale.SIMPLIFIED_CHINESE.equals(locale) || Locale.ENGLISH.equals(locale)) {
            return true;
        }
        return false;
    }


    private String getFormat(HttpServletRequest request) {
        String messageFormat = request.getParameter(SystemParameterNames.getFormat());
        if (messageFormat == null) {
            return MessageFormat.xml.name();
        } else {
            return messageFormat;
        }
    }


    /**
     * 获取真实IP地址
     * 1. 服务器采用负载均衡时, 则先判断X_REAL_IP头文件, 请求IP会变成负载均衡服务器的IP。
     * 2. 根据X_FORWARDED_FOR取头文件IP地址。
     *    采集顺序规则(PROXY_CLIENT_IP, WL_PROXY_CLIENT_IP, HTTP_CLIENT_IP, HTTP_X_FORWARDED_IP)
     *    真实IP：取出IP为 192.168.1.110, 192.168.1.120, 192.168.1.130, 192.168.1.100， 则真实IP为: 192.168.1.110
     * 3. 采用request.getRemoteAddr()远程IP地址。
     *    如为远程IP地址为(127.0.0.1 || 0:0:0:0:0:0:0:1), 则以本机网卡IP地址为准。
     * @param request
     * @return
     */
    private String getRemoteAddr(HttpServletRequest request) {
        //默认Ip，为设备到远程服务器IP
        String defaultIp = "0:0:0:0";
        //1.首先判断载均衡服务器的IP
        String remoteIp = request.getHeader(Constants.X_REAL_IP);
        if (StringUtils.hasText(remoteIp)) {
            return remoteIp;
        }
        //2.根据X_FORWARDED_FOR取各种IP地址
        remoteIp = request.getHeader(Constants.X_FORWARDED_FOR);
        if(remoteIp == null || remoteIp.length() == 0 || "unknown".equalsIgnoreCase(remoteIp)) {
            remoteIp = request.getHeader(Constants.PROXY_CLIENT_IP);
        }
        if(remoteIp == null || remoteIp.length() == 0 || "unknown".equalsIgnoreCase(remoteIp)) {
            remoteIp = request.getHeader(Constants.WL_PROXY_CLIENT_IP);
        }
        if (remoteIp == null || remoteIp.length() == 0 || "unknown".equalsIgnoreCase(remoteIp)) {
            remoteIp = request.getHeader(Constants.HTTP_CLIENT_IP);
        }
        if (remoteIp == null || remoteIp.length() == 0 || "unknown".equalsIgnoreCase(remoteIp)) {
            remoteIp = request.getHeader(Constants.HTTP_X_FORWARDED_IP);
        }
        if (StringUtils.hasText(remoteIp)) {
            String[] ips = remoteIp.split(",");
            for (String firstIp : ips) {
                if (!"null".equalsIgnoreCase(firstIp)) {
                    return firstIp;
                }
            }
        } else {
            //3. 采用request.getRemoteAddr()远程IP地址。
            remoteIp = request.getRemoteAddr();
            if(StringUtils.hasText(remoteIp)){
                //根据网卡取本机配置的IP
                if(remoteIp.equals("127.0.0.1") || remoteIp.equals("0:0:0:0:0:0:0:1")){
                    try {
                        InetAddress inetAddress = InetAddress.getLocalHost();
                        remoteIp = inetAddress.getHostAddress();
                        if(!StringUtils.hasText(remoteIp)){
                            return defaultIp;
                        }
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                        logger.error("getRemoteAddr-->>getLocalHost()-->>UnknownHostException " + e.getMessage());
                    }
                }
                return remoteIp;
            }
        }
        return defaultIp;
    }



    private Map<String, String> getRequestParams(HttpServletRequest request) {
        Map<String, String[]> srcParamMap = request.getParameterMap();
        HashMap<String, String> destParamMap = new HashMap<String, String>(srcParamMap.size());
        for (String obj : srcParamMap.keySet()) {
            String[] values = srcParamMap.get(obj);
            if (values != null && values.length > 0) {
                destParamMap.put(obj, values[0]);
            } else {
                destParamMap.put(obj, null);
            }
        }
        return destParamMap;

    }

    private BindingResult doBind(HttpServletRequest webRequest, Class<? extends XboxRequest> requestType, MessageFormat format) {
        XboxRequest bindObject = null;
        if (format == MessageFormat.xml) {
            bindObject = xmlConverter.getObject(webRequest.getParameter(SystemParameterNames.getBindObject()), requestType);
        } else {
            bindObject = jackJosnConverter.getObject(webRequest.getParameter(SystemParameterNames.getBindObject()), requestType);
        }
        DataBinder dataBinder = new DataBinder(bindObject);
//        dataBinder.bind();
        dataBinder.setValidator(getValidator());
        dataBinder.validate();
        return dataBinder.getBindingResult();
    }


    private Validator getValidator() {
        if (this.validator == null) {
            LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
            localValidatorFactoryBean.afterPropertiesSet();
            this.validator = localValidatorFactoryBean;
        }
        return this.validator;
    }

    private class DefaultXboxRequest extends AbstractXboxRequest {


    }
}
